package com.zlq.cn.service.tian;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.retrofit.Retrofit2ConverterFactory;
import com.zlq.cn.annotations.TianApiDataSave;
import com.zlq.cn.api.TianApiResult;
import com.zlq.cn.config.OkhttpConfig;
import com.zlq.cn.dto.vo.TianListResult;
import com.zlq.cn.entity.TianDataSave;
import com.zlq.cn.entity.TianStarinfo;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.RequestBody;
import okio.Timeout;
import one.util.streamex.StreamEx;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import retrofit2.*;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Slf4j
@Component
public class TianConfigService {
    private static final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    private static final String KEY = "37e9fec81b9509b533db75d366a9b80c";

    private static final String KEY_API_NO = "apiNo";

    private static final String UNX_KEY = "unxKey";

    @Resource
    private TianDataSaveService tianDataSaveService;

    @Bean("tianApiInterceptor")
    public Interceptor tianApiInterceptor() {
        return chain -> {
            Request request = chain.request();
            Invocation invocation = request.tag(Invocation.class);
            Class<?> aClass = Optional.ofNullable(invocation).map(Invocation::method).map(Method::getDeclaringClass).orElse(null);
            boolean assignableFrom = aClass != null && aClass.isAssignableFrom(TianApiService.class);
            if (assignableFrom) {
                String requestMethod = request.method();
                if ("GET".equalsIgnoreCase(requestMethod)) {
                    request = request.newBuilder()
                            .url(request.url().newBuilder()
                                    .addEncodedQueryParameter("key", KEY)
                                    .build())
                            .build();
                } else if ("POST".equalsIgnoreCase(requestMethod)) {
                    RequestBody body = request.body();
                    if (body instanceof FormBody) {
                        FormBody formBody = ((FormBody) body);
                        FormBody.Builder builder = new FormBody.Builder();
                        for (int i = 0; i < formBody.size(); i++) {
                            builder.addEncoded(formBody.name(i), formBody.value(i));
                        }
                        builder.addEncoded("key", KEY);
                        request.newBuilder()
                                .method("POST", formBody)
                                .build();
                    }
                }
            }
            return chain.proceed(request);
        };
    }

    @Bean("tianApiService")
    public TianApiService xx() {
        TianApiService tianApiService = new Retrofit.Builder()
                .baseUrl("https://apis.tianapi.com")
                .client(OkhttpConfig.okHttpClient)
                .addConverterFactory(Retrofit2ConverterFactory.create())
                .build()
                .create(TianApiService.class);
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(tianApiService);
        tianApiService = (TianApiService) Proxy.newProxyInstance(
                TianApiService.class.getClassLoader(),
                new Class<?>[]{TianApiService.class},
                new InvocationHandler() {

                    @Override
                    public @Nullable
                    Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
                        Object result = invocationHandler.invoke(proxy, method, args);
                        TianApiDataSave tianApiDataSave = method.getAnnotation(TianApiDataSave.class);
                        if (tianApiDataSave != null) {
                            // 接口唯一值key
                            String unxKey = tianApiDataSave.unxKey();
                            String type = tianApiDataSave.type();
                            String[] parameterNames = defaultParameterNameDiscoverer.getParameterNames(method);
                            if (StringUtils.isEmpty(unxKey) && args != null && args.length > 0) {
                                for (int i = 0; i < parameterNames.length; i++) {
                                    Object argValue = args[i];
                                    if (Objects.equals(UNX_KEY, parameterNames[i])) {
                                        unxKey = argValue.toString();
                                    } else if (StringUtils.isEmpty(unxKey) && argValue instanceof Map) {
                                        Object o = ((Map<?, ?>) argValue).get(UNX_KEY);
                                        if (Objects.nonNull(o)) {
                                            unxKey = o.toString();
                                        }
                                    }
                                }
                            }
                            if (StringUtils.isEmpty(type) && args != null && args.length > 0) {
                                for (int i = 0; i < parameterNames.length; i++) {
                                    if (Objects.equals(KEY_API_NO, parameterNames[i])) {
                                        type = args[i].toString();
                                    }
                                }
                            }
                            Assert.state(StringUtils.isNotEmpty(type), "接口编号不能为空");
                            String finalUnxKey = unxKey;
                            String finalType = type;
                            result = new ExecutorCallbackCall((Call<TianApiResult>) result, (body) -> {
                                TianApiResult tianApiResult = (TianApiResult) body;
                                if (tianApiResult.isOk()){
                                    Object data = tianApiResult.getResult();
                                    List list = new ArrayList();
                                    if (data instanceof JSONObject) {
                                        Object resultList = ((JSONObject) data).get("list");
                                        if (resultList instanceof JSONArray) {
                                            list.addAll((Collection) resultList);
                                        } else {
                                            list.add(data);
                                        }
                                    } else if (data instanceof JSONArray) {
                                        list.addAll((Collection) data);
                                    } else if (data instanceof TianListResult) {
                                        list.addAll(((TianListResult<?>) data).getList());
                                    } else {
                                        list.add(data);
                                    }
                                    saveListDatas(finalType, finalUnxKey, list);
                                }
                            });
                        }
                        return result;
                    }
                });
        return tianApiService;
    }

    private void saveListDatas(String type, String unxKey, List list) {
        List<TianDataSave> tianDataSaves = new ArrayList<>();
        // 不等于空去重
        if (StringUtils.isNotEmpty(unxKey)) {
            String unxNames = StreamEx.of(list)
                    .map(item -> String.format("'%s'", ((JSONObject) item).getString(unxKey)))
                    .joining(",");
            Set<String> extSet = tianDataSaveService.lambdaQuery()
                    .select(TianDataSave::getUnxName)
                    .eq(TianDataSave::getType, type)
                    .inSql(TianDataSave::getUnxName, unxNames)
                    .list().stream().map(TianDataSave::getUnxName).collect(Collectors.toSet());
            for (Object item : list) {
                String unxValue = ((JSONObject) item).getString(unxKey);
                if (!extSet.contains(unxValue)) {
                    tianDataSaves.add(new TianDataSave()
                            .setData(JSON.toJSONString(item))
                            .setType(type)
                            .setUnxName(unxValue)
                            .setCreateTime(LocalDateTime.now()));
                }
            }
        } else {
            for (Object item : list) {
                tianDataSaves.add(new TianDataSave()
                        .setData(JSON.toJSONString(item))
                        .setType(type)
                        .setCreateTime(LocalDateTime.now()));
            }
        }
        tianDataSaveService.saveBatch(tianDataSaves);
    }

    private static class ExecutorCallbackCall<T> implements Call<T> {
        private final Consumer<T> function;
        private final Call<T> delegate;

        public ExecutorCallbackCall(Call<T> delegate, Consumer<T> function) {
            this.delegate = delegate;
            this.function = function;
        }

        @Override
        public Response<T> execute() throws IOException {
            Response<T> response = delegate.execute();
            function.accept(response.body());
            return response;
        }

        @Override
        public void enqueue(Callback<T> callback) {
            delegate.enqueue(callback);
        }

        @Override
        public boolean isExecuted() {
            return delegate.isExecuted();
        }

        @Override
        public void cancel() {
            delegate.cancel();
        }

        @Override
        public boolean isCanceled() {
            return delegate.isCanceled();
        }

        @Override
        public Call<T> clone() {
            return delegate.clone();
        }

        @Override
        public Request request() {
            return delegate.request();
        }

        @Override
        public Timeout timeout() {
            return delegate.timeout();
        }
    }

    public static Map<String, String> paramConvert(Map<String, Object> param) {
        Map<String, String> map = new HashMap<>();
        if (MapUtil.isNotEmpty(param)) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                if (ObjectUtils.isNotEmpty(entry.getValue())) {
                    map.put(entry.getKey(), entry.getValue().toString());
                }
            }
        }
        return map;
    }

    public interface TianApiService {

        /**
         * 明星百科大全
         */
        @GET("/starinfo/index")
        Call<TianApiResult<TianListResult<TianStarinfo>>> starinfo(@Query("name") String name,
                                                                   @Query("sex") String sex,
                                                                   @Query("nation") String nation,
                                                                   @Query("birth") String birth,
                                                                   @Query("constellation") String constellation,
                                                                   @Query("page") String page,
                                                                   @Query("num") String num);

        /**
         * 所有公共接口
         */
        @TianApiDataSave
        @GET("/{apiNo}/index")
        Call<TianApiResult<Object>> tianCommonApi(@Path(KEY_API_NO) String apiNo, @QueryMap Map<String, String> param);

        /**
         * 经典台词
         */
        @TianApiDataSave(type = "dialogue")
        @GET("/dialogue/index")
        Call<TianApiResult<JSONObject>> dialogue();

        /**
         * 神回复
         */
        @TianApiDataSave(type = "godreply")
        @GET("/godreply/index")
        Call<TianApiResult<TianListResult<JSONObject>>> godreply(@QueryMap Map<String, String> param);

        /**
         * 歇后语
         */
        @GET("/xiehou/index")
        @TianApiDataSave(type = "xiehou")
        Call<TianApiResult<TianListResult<JSONObject>>> xiehou(@QueryMap Map<String, String> param);

        /**
         * 网络流行语
         */
        @GET("/hotword/index")
        @TianApiDataSave(type = "hotword")
        Call<TianApiResult<TianListResult<JSONObject>>> hotword(@QueryMap Map<String, String> param);

        /**
         * 脑筋急转弯
         */
        @GET("/naowan/index")
        @TianApiDataSave(type = "naowan")
        Call<TianApiResult<TianListResult<JSONObject>>> naowan(@QueryMap Map<String, String> param);

        /**
         * 雷人笑话
         */
        @GET("/joke/index")
        @TianApiDataSave(type = "joke")
        Call<TianApiResult<TianListResult<JSONObject>>> joke(@QueryMap Map<String, String> param);

        /**
         * 朋友圈文案
         */
        @GET("/pyqwenan/index")
        @TianApiDataSave(type = "pyqwenan")
        Call<TianApiResult<JSONObject>> pyqwenan(@QueryMap Map<String, String> param);

        /**
         * 失恋分手句子
         */
        @GET("/hsjz/index")
        @TianApiDataSave(type = "hsjz")
        Call<TianApiResult<JSONObject>> hsjz(@QueryMap Map<String, String> param);

        /**
         * 彩虹屁
         */
        @GET("/caihongpi/index")
        @TianApiDataSave(type = "caihongpi")
        Call<TianApiResult<JSONObject>> caihongpi(@QueryMap Map<String, String> param);

        /**
         * 舔狗日记
         */
        @GET("/tiangou/index")
        @TianApiDataSave(type = "tiangou")
        Call<TianApiResult<JSONObject>> tiangou(@QueryMap Map<String, String> param);

        /**
         * 早安心语
         */
        @GET("/zaoan/index")
        @TianApiDataSave(type = "zaoan")
        Call<TianApiResult<JSONObject>> zaoan(@QueryMap Map<String, String> param);

        /**
         * 晚安心语
         */
        @GET("/wanan/index")
        @TianApiDataSave(type = "wanan")
        Call<TianApiResult<JSONObject>> wanan(@QueryMap Map<String, String> param);

        /**
         * 毒鸡汤
         */
        @GET("/dujitang/index")
        @TianApiDataSave(type = "dujitang")
        Call<TianApiResult<JSONObject>> dujitang(@QueryMap Map<String, String> param);
    }
}

